/*
 * This file is part of ddesktop: remote desktop system for iPad 
 *
 * Copyright (C) 2010 Kazuhiro Satoh
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. if not, see <http://www.gnu.org/licenses/>.
 */

#import "Setting.h"


@interface SettingItemObserver : NSObject
{
	id _target;
	SEL _action;
}
@property (nonatomic,readonly) id target;
@property (nonatomic,readonly) SEL action;
@end

@implementation SettingItemObserver

@synthesize target = _target;
@synthesize action = _action;

-(id) initWithTarget:(id)target action:(SEL)action
{
	if ((self = [super init])){
		_target = target;
		_action = action;
	}
	return self;
}
@end



@interface SettingItem()
+(SettingItem*) settingItemWithType:(NSString*)type;
-(void)notifyToObservers;
@property (nonatomic,readwrite,assign) NSString* type;
@property (nonatomic,readwrite,assign) NSMutableArray* observers;
@property (nonatomic,readwrite,assign) NSMutableDictionary* dataDict;
@end

@implementation SettingItem

@synthesize observers = _observers;
@synthesize dataDict = _dataDict;


+(SettingItem*) settingItemWithType:(NSString*)type
{
	if([type isEqualToString:@"SettingDirectory"]){
		return [[[SettingDirectory alloc] init] autorelease];
	}
	if([type isEqualToString:@"SettingItem"]){
		return [[[SettingItem alloc] init] autorelease];
	}
	
	return nil;
}


- (id)copyWithZone:(NSZone *)zone
{
	SettingItem* clone = [[[self class] alloc]init];
	if(self.dataDict != nil){
		clone.dataDict = [[NSMutableDictionary allocWithZone:zone] initWithDictionary:self.dataDict copyItems:YES];
	}
	if(self.observers != nil){
		clone.observers = [self.observers copyWithZone:zone];
	}
    return  clone;
}


-(void)setValue:(id)value
{
	[_dataDict setObject:value forKey:@"_value_"];

	[self notifyToObservers];
}
-(NSObject*)value
{
	return [_dataDict objectForKey:@"_value_"];
}


-(void)setType:(NSString*)type
{
	[_dataDict setObject:type forKey:@"_type_"];
}
-(NSObject*)type
{
	return [_dataDict objectForKey:@"_type_"];
}


-(void)notifyToObservers
{
	if(_observers != nil){
		for(int i = 0; i < [_observers count]; i++){
			SettingItemObserver* observer = [_observers objectAtIndex:i];
			[observer.target performSelector:observer.action withObject:self.value];
		}
	}
}


-(id)initWithValue:(NSObject*)value
{
	if (self = [self init]){
		[self setValue:value];
	}
	return self;
}


-(id)init
{
	if ((self = [super init])){
		_dataDict = [[NSMutableDictionary alloc] init];
		[self setType:@"SettingItem"];
		_observers = nil;
	}
	return self;
}


-(void)dealloc
{
	if (_dataDict != nil){
		[_dataDict release];
	}
	if(_observers != nil){
		[_observers release];
	}
	
	[super dealloc];
}


-(void)addObserver:(id)observer action:(SEL)action
{
	if(_observers == nil){
		_observers = [[NSMutableArray alloc] init];
	}
	[_observers addObject:[[SettingItemObserver alloc] initWithTarget:observer action:action]];
}


-(void)addMetaData:(NSObject*)metadata forKey:(NSString*)key
{
	[_dataDict setObject:metadata forKey:[NSString stringWithFormat:@"_meta_%@_", key]];
}


-(NSObject*)metaData:(NSString*)key
{
	return [_dataDict objectForKey:[NSString stringWithFormat:@"_meta_%@_", key]];
}

@end



@interface SettingDirectory()
@property (nonatomic,readonly) NSMutableArray* itemKeyList;
@end

@implementation SettingDirectory


-(id)init
{
	if ((self = [super init])){
		[_dataDict setObject:[[NSMutableArray alloc] init] forKey:@"_itemList_"];
		[self setType:@"SettingDirectory"];
	}
	return self;
}


-(NSMutableArray*)itemKeyList
{
	return [_dataDict objectForKey:@"_itemList_"];
}


-(void)addItemWithoutKey:(SettingItem*)item
{
	[self addItem:item forKey:[NSString stringWithFormat:@"_item_[%d]", [self itemCount]]];
}


-(void)addItem:(SettingItem*)item forKey:(NSString*)key
{
	[_dataDict setObject:item forKey:key];
	[self.itemKeyList addObject:key];
	
	[super notifyToObservers];
}


-(SettingItem*)itemAtIndex:(int)index
{
	return [self itemForKey:[self.itemKeyList objectAtIndex:index]];
}


-(SettingItem*)itemForKey:(NSString*)key
{
	return [_dataDict objectForKey:key];
}


-(void)replaceItemAtIndex:(int)index withItem:(SettingItem*)item
{
	[self replaceItemForKey:[self.itemKeyList objectAtIndex:index] withItem:item];
}


-(void)replaceItemForKey:(NSString*)key withItem:(SettingItem*)item
{
	[_dataDict setObject:item forKey:key];
	[super notifyToObservers];
}


-(void)removeItemForKey:(NSString*)key
{
	[self removeItemAtIndex:[self.itemKeyList indexOfObject:key]];
}


-(void)removeItemAtIndex:(int)index
{
	[_dataDict removeObjectForKey:[self.itemKeyList objectAtIndex:index]];
	[self.itemKeyList removeObjectAtIndex:index];
	
	for(int i = index; i < [self itemCount]; i++){
		NSString* key = [self.itemKeyList objectAtIndex:i];
		if([[key substringToIndex:6] isEqualToString:@"_item_"]){
			SettingItem* item = [self itemAtIndex:i];
			NSString* newKey = [NSString stringWithFormat:@"_item_[%d]", i];
			[_dataDict setObject:item forKey:newKey];
			[self.itemKeyList replaceObjectAtIndex:i withObject:newKey];
			[_dataDict removeObjectForKey:key];
		}
	}
	
	[super notifyToObservers];
}


-(NSArray*)allItems
{
	NSMutableArray* array = [[NSMutableArray alloc] init];
	
	for (int i = 0; i < self.itemCount; i++) {
		[array addObject:[self itemForKey:[self.itemKeyList objectAtIndex:i]]];
	}
	return [array autorelease];
}


-(NSArray*)allKeys
{
	return self.itemKeyList;
}


-(int)itemCount
{
	NSArray* list = self.itemKeyList;
	return [list count];
}

@end



@interface Setting()
-(void)expandDictionary:(NSMutableDictionary*)dict toDirectory:(SettingItem*)dir;
-(void)collapseDirectory:(SettingItem*)dir toDictionary:(NSMutableDictionary*)dict;
@end


@implementation Setting


-(id)initWithFilePath:(NSString*)path
{
    if ((self = [super init])){
		_settingFilePath = [[NSString stringWithString:path] retain];
		
		{ // load setting file
			_rootDirectory = [[SettingDirectory alloc] init];
			NSMutableDictionary* dict = [[[NSMutableDictionary alloc] initWithContentsOfFile:_settingFilePath] autorelease];
			if(dict != nil){
				[self expandDictionary:dict toDirectory:_rootDirectory];
			}
		}
	}
	return self;
}


-(void)dealloc
{
	{
		NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease];
		[self collapseDirectory:_rootDirectory toDictionary:dict];
		if(![dict writeToFile:_settingFilePath atomically:YES]){
			NSLog(@"failed to write setting file");
		}
	}
	[_rootDirectory release];
	[_settingFilePath release];
	
	[super dealloc];
}


-(void)expandDictionary:(NSMutableDictionary*)dict toDirectory:(SettingItem*)dir
{
	NSArray* keys = [dict allKeys];
	
	for(int i = 0; i < [keys count]; i++){
		NSString* key = (NSString*)[keys objectAtIndex:i];
		NSObject* val = [dict objectForKey:[keys objectAtIndex:i]];
		
		if([val isKindOfClass:[NSDictionary class]]){
			SettingItem* newDir = [SettingItem settingItemWithType:[(NSDictionary*)val objectForKey:@"_type_"]];
			if(newDir != nil){
				[self expandDictionary:(NSMutableDictionary*)val toDirectory:newDir];
				[dir.dataDict setObject:newDir forKey:key];
			}
		}
		else{
			[dir.dataDict setObject:val forKey:key];
		}
	}
}


-(void)collapseDirectory:(SettingItem*)dir toDictionary:(NSMutableDictionary*)dict
{
	NSArray* keys = [dir.dataDict allKeys];
	
	for(int i = 0; i < [keys count]; i++){
		NSString* key = [keys objectAtIndex:i];
		NSObject* val = [dir.dataDict objectForKey:key];
		
		if([val isKindOfClass:[SettingItem class]]){
			NSMutableDictionary* newDict = [[[NSMutableDictionary alloc] init] autorelease];
			[self collapseDirectory:(SettingItem*)val toDictionary:newDict];
			[dict setObject:newDict forKey:key];
		}
		else{
			[dict setObject:val forKey:key];
		}
	}
}


-(void)setDefalutItem:(SettingItem*)item forKeyPath:(NSString*)keyPath
{
	NSArray* pathArray = [keyPath componentsSeparatedByString:@"."];
	SettingDirectory* currentDir;
	SettingDirectory* nextDir;
	
	currentDir = _rootDirectory;
	for(int i = 0; i < [pathArray count] - 1; i++){
		NSString* dirName = [pathArray objectAtIndex:i];
		nextDir = (SettingDirectory*)[currentDir itemForKey:dirName];
		if (nextDir == nil) {
			nextDir = [[[SettingDirectory alloc] init] autorelease];
			[currentDir addItem:nextDir forKey:dirName];
		}
		currentDir = nextDir;
	}
	
	if([currentDir itemForKey:[pathArray lastObject]] == nil){
		[currentDir addItem:item forKey:[pathArray lastObject]];
	}
}


-(void)addDirectoryForKeyPath:(NSString*)keyPath
{
	NSArray* pathArray = [keyPath componentsSeparatedByString:@"."];
	SettingDirectory* currentDir;
	SettingDirectory* nextDir;
	
	currentDir = _rootDirectory;
	for(int i = 0; i < [pathArray count]; i++){
		NSString* dirName = [pathArray objectAtIndex:i];
		nextDir = (SettingDirectory*)[currentDir itemForKey:dirName];
		if (nextDir == nil) {
			nextDir = [[[SettingDirectory alloc] init] autorelease];
			[currentDir addItem:nextDir forKey:dirName];
		}
		currentDir = nextDir;
	}
}


-(SettingItem*)itemForKeyPath:(NSString*)keyPath
{
	NSArray* pathArray = [keyPath componentsSeparatedByString:@"."];
	SettingDirectory* dir;
	
	dir = _rootDirectory;
	for(int i = 0; i < [pathArray count] - 1; i++){
		NSString* dirName = [pathArray objectAtIndex:i];
		dir = (SettingDirectory*)[dir itemForKey:dirName];
		if (dir == nil) {
			return nil;
		}
	}
	
	return (SettingDirectory*)[dir itemForKey:[pathArray lastObject]];
}


-(SettingDirectory*)directoryForKeyPath:(NSString*)keyPath
{
	return (SettingDirectory*)[self itemForKeyPath:keyPath];
}


@end
